﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel.Composition;
using BingMapBrowser.CrmSdk;
using Microsoft.Maps.MapControl;

namespace BingMapBrowser.Data
{
	[Export(typeof(IEntityDataService))]
	public class EntityDataService : IEntityDataService
	{
		ObservableCollection<MyEntity> Records = new ObservableCollection<MyEntity>();
		ObservableCollection<MyEntity> TempMetadata;

		public void QueryData(ObservableCollection<MyEntity> metadata)
		{
			TempMetadata = metadata;
			Records.Clear();
			foreach (var entity in metadata)
			{
				QueryData(entity);
			}
		}

		private void QueryData(MyEntity entity)
		{
			//Generate FetchXml
			//Retrieve Data
			//Load Data in records list

			FetchExpression fetch = new FetchExpression();
			fetch.Query = GetFetchXml(entity);
			MyOrganizationServiceClient svc = SilverlightUtility.GetSoapService() as MyOrganizationServiceClient;
			svc.EntityDisplayName = entity.EntityDisplayName;
			svc.RetrieveMultipleCompleted += new EventHandler<RetrieveMultipleCompletedEventArgs>(svc_RetrieveMultipleCompleted);
			svc.RetrieveMultipleAsync(fetch);
		}

		public void svc_RetrieveMultipleCompleted(object sender, RetrieveMultipleCompletedEventArgs e)
		{
			try
			{
				if (e.Error == null)
				{
					SilverlightUtility.Trace("Count: " + e.Result.Entities.Count);
					//ToDo: P0: Manage Paging.
					ObservableCollection<Entity> entities = e.Result.Entities;
					string displayName = ((MyOrganizationServiceClient)sender).EntityDisplayName;
					foreach (var entity in entities)
					{
						MyEntity item = MyEntity.CreateClone(displayName, TempMetadata);
						item.EntityName = e.Result.EntityName;
						item.RecordId = entity.Id;
						item.Address.Location = new Location(Double.Parse(GetAttribute(entity, item.Address.Latitude)), Double.Parse(GetAttribute(entity, item.Address.Longitude)));
						foreach (var link in item.Links)
						{
							link.Value = GetAttribute(entity, link.FieldName);
						}
						foreach (var listItem in item.ListItems)
						{
							listItem.Value = GetAttribute(entity, listItem.FieldName);
						}
						item.ToolTip.Value = GetAttribute(entity, item.ToolTip.FieldName);
						Records.Add(item);
					}
					if (RecordsChanged != null)
					{
						RecordsChanged(Records);
					}
				}
				else
					MessageBox.Show("Error in retrieve multiple: " + e.Error.Message);
			}
			catch (Exception ex)
			{
				MessageBox.Show("Exception Occured in RetrieveMultipleCompeted event.\nErrorMessage: " + ex.Message);
			}
		}

		public event Action<IEnumerable<MyEntity>> RecordsChanged;


		#region Private Helpers
		private static ObservableCollection<string> GetAllRequiredColumns(MyEntity entity)
		{
			ObservableCollection<string> columns = new ObservableCollection<string>();
			columns.Add(entity.Address.Latitude);
			columns.Add(entity.Address.Longitude);

			foreach (var link in entity.Links)
			{
				if (!String.IsNullOrEmpty(link.FieldName))
					columns.Add(link.FieldName);
			}

			foreach (var listItem in entity.ListItems)
			{
				if (!string.IsNullOrEmpty(listItem.FieldName))
					columns.Add(listItem.FieldName);
			}
			columns.Add(entity.ToolTip.FieldName);

			return columns;
		}

		private static string GetFetchXml(MyEntity entity)
		{
			string actualFetch = entity.FetchXml;
			List<string> args = new List<string>();
			foreach (var condition in entity.Conditions)
			{
				if (String.IsNullOrEmpty(condition.Value))
				{
					args.Add(String.Empty);
				}
				else
				{
					string actCondition = String.Format(condition.Expression, condition.Value);
					args.Add(actCondition);
				}
			}
			actualFetch = String.Format(actualFetch, args.ToArray());
			return actualFetch;
		}

		//private static FilterExpression GetFilterExpression(MyEntity entity)
		//{
		//    FilterExpression filterExp = new FilterExpression();
		//    ObservableCollection<ConditionExpression> conditions = new ObservableCollection<ConditionExpression>();

		//    ConditionExpression condition = null;
		//    foreach (var filter in entity.Filters)
		//    {
		//        if (!String.IsNullOrEmpty(filter.FieldName))
		//        {
		//            switch (filter.Type)
		//            {
		//                case "user":
		//                    condition = CreateUserTypeFilter(filter);
		//                    if (condition != null)
		//                    {
		//                        conditions.Add(condition);
		//                    }
		//                    break;
		//                default:
		//                    condition = CreateStringTypeFilter(filter);
		//                    if (condition != null)
		//                    {
		//                        conditions.Add(condition);
		//                    }
		//                    break;
		//            }
		//        }
		//    }
		//    filterExp.Conditions = conditions;
		//    filterExp.FilterOperator = LogicalOperator.And;
		//    return filterExp;
		//}

		//private static ConditionExpression CreateUserTypeFilter(Filter filter)
		//{
		//    ConditionExpression condition = new ConditionExpression();
		//    condition.AttributeName = filter.FieldName;
		//    if (String.IsNullOrEmpty(filter.Value))
		//        return null;
		//    switch (filter.Value)
		//    {
		//        case "me":
		//            condition.Operator = ConditionOperator.EqualUserId;
		//            break;
		//        case "all":
		//        default:
		//            return null;
		//    }
		//    return condition;
		//}

		//private static ConditionExpression CreateStringTypeFilter(Filter filter)
		//{
		//    ConditionExpression condition = new ConditionExpression();
		//    condition.AttributeName = filter.FieldName;
		//    if (string.IsNullOrEmpty(filter.Value))
		//        return null;
		//    switch (filter.Operator)
		//    {
		//        case "ge":
		//            condition.Operator = ConditionOperator.GreaterEqual;
		//            break;
		//        case "like":
		//            condition.Operator = ConditionOperator.Like;
		//            break;
		//        default:
		//            return null;
		//    }

		//    switch (filter.Type)
		//    {
		//        case "string":
		//            condition.Values = new ObservableCollection<object>() { filter.Value };
		//            break;
		//        case "double":
		//            condition.Values = new ObservableCollection<object>() { Double.Parse(filter.Value) };
		//            break;
		//        case "int":
		//            condition.Values = new ObservableCollection<object>() { Int32.Parse(filter.Value) };
		//            break;
		//        case "decimal":
		//            condition.Values = new ObservableCollection<object>() { Decimal.Parse(filter.Value) };
		//            break;
		//        default:
		//            return null;
		//    }

		//    return condition;
		//}

		private static string GetAttribute(Entity entity, string attributeName)
		{
			try
			{
				foreach (var attr in entity.Attributes)
				{
					if (attr.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase))
					{
						return  GetAttributeActualValue(attr.Value);
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show("Error Occured in GetAttribute: " + e.Message);
			}
			return "0";
		}

		private static string GetAttributeActualValue(object attr)
		{
			if((attr as EntityReference) != null)
			{
				EntityReference refValue = (EntityReference)attr;
				return String.IsNullOrEmpty(refValue.Name) ? refValue.LogicalName : refValue.Name;
			}

			if ((attr as AliasedValue) != null)
			{
				AliasedValue aliasedValue = (AliasedValue)attr;
				return GetAttributeActualValue(aliasedValue.Value);
			}

			if ((attr as CrmSdk.Money) != null)
			{
				Money moneyValue = (Money)attr;
				return moneyValue == null ? "0" : moneyValue.Value.ToString();
			}

			if ((attr as CrmSdk.OptionSetValue) != null)
			{
				OptionSetValue osv = (OptionSetValue)attr;
				return osv.Value == 0 ? "0" : osv.Value.ToString();
			}
			return attr.ToString();
		}

		private static void ShowAllRetrieveAttributes(Entity entity)
		{
			foreach (var attr in entity.Attributes)
			{
				
			}
		}
		#endregion

	}
}
